package com.noassertions.web;

import java.util.ArrayList;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.noassertions.data.DAOInterface;
import com.noassertions.domain.Fit;
import com.noassertions.domain.Rider;
import com.noassertions.domain.User;
import com.noassertions.services.KdTreeService;
import com.noassertions.utils.BeanUtil;

import edu.wlu.cs.levy.CG.KeySizeException;

@Controller
public class SimilarController
{
    protected final Log log = LogFactory.getLog(getClass());
    private final DAOInterface dao;
    private final KdTreeService kDTreeService;

    @Autowired
    public SimilarController(DAOInterface dao, KdTreeService kDTreeService) {
        this.dao = dao;
        this.kDTreeService = kDTreeService;
    }    

	@RequestMapping(value = "/similar/rider/{userid}", method = RequestMethod.GET)
	public String getSimilarRider(@PathVariable Integer userid, Model model) throws KeySizeException, IllegalArgumentException
	{
		Rider rider = dao.getRiderByID(userid);
		model.addAttribute("rider", rider);
		model.addAttribute("lrangerider", BeanUtil.getLowerRange(rider, .015f));
		model.addAttribute("urangerider", BeanUtil.getUpperRange(rider, .015f));
		
		// TODO this business of checking for a string (which indicates that user is not authenticated) is a total hack and should be factored out
		if (! (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof String)) {
			UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
			User user = dao.getUserByemailAddress(principal.getUsername());
			model.addAttribute("emailAddress", user.getEmailaddress());
		}
		
		return "similarrider";
	}	

	@RequestMapping(value = "/similar/rider/{userid}", method = RequestMethod.POST)
	public String postSimilarRider(@PathVariable Integer userid, Model model) throws KeySizeException, IllegalArgumentException
	{
		Rider rider = dao.getRiderByID(userid);
		ArrayList similarRiders = kDTreeService.getSimilarRiders(rider);
		model.addAttribute("similarRiders", similarRiders);
		
		// TODO this business of checking for a string (which indicates that user is not authenticated) is a total hack and should be factored out
		if (! (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof String)) {
			UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
			User user = dao.getUserByemailAddress(principal.getUsername());
			model.addAttribute("emailAddress", user.getEmailaddress());
		}
		
		return "similarriderresults";
	}
	
	@RequestMapping(value = "/similar/rider/range", method = RequestMethod.POST)
	public String postSimilarRiderRange(Model model, HttpServletRequest request) throws KeySizeException, IllegalArgumentException
	{
//		Rider rider = dao.getRiderByID(userid);
		
		
		//build lower array, build upper arry
		// pass to kdTreeService which has a method that users range(double[] lowk,double[] uppk)
		
		Rider lRangeRider = new Rider();
		Rider uRangeRider = new Rider();
		
		//loop through all elements of form
		//assign ltotalbodyheight to lower array and utotalbodyheight to upper array
		// if checkbox is encountered, set it as a flag that we'll check later
		
		boolean totalBodyHeightFlag = false;
		boolean sternalNotchFlag = false;
		boolean shoulderWidthFlag = false;
		boolean trunkFlag = false;
		boolean humerusFlag = false;
		boolean forearmFlag = false;
		boolean totalArmFlag = false;
		boolean femureFlag = false;
		boolean tibiaFlag = false;
		boolean inseamFlag = false;
		boolean footLengthFlag = false;

		Enumeration namesEnum = request.getParameterNames();
		while (namesEnum.hasMoreElements()) {
			String name = (String)namesEnum.nextElement();
			String value = request.getParameter(name);
	
			if("totalbodyheight".equals(name)) {totalBodyHeightFlag = true;}
			if("sternalnotch".equals(name)) {sternalNotchFlag = true;}
			if("shoulderwidth".equals(name)) {sternalNotchFlag = true;}
			if("trunk".equals(name)) {trunkFlag = true;}
			if("humerus".equals(name)) {humerusFlag = true;}
			if("forearm".equals(name)) {forearmFlag = true;}
			if("totalarm".equals(name)) {totalArmFlag = true;}
			if("femur".equals(name)) {femureFlag = true;}
			if("tibia".equals(name)){tibiaFlag = true;}
			if("inseam".equals(name)) {inseamFlag = true;}											
			if("footlength".equals(name)) {footLengthFlag = true;}											

			if (name.equals("ltotalbodyheight")) {lRangeRider.setTotalbodyheight(Float.valueOf(value));}
			if (name.equals("lsternalnotch")) {lRangeRider.setSternalnotch(Float.valueOf(value));}
			if (name.equals("lshoulderwidth")) {lRangeRider.setShoulderwidth(Float.valueOf(value));}
			if (name.equals("ltrunk")) {lRangeRider.setTrunk(Float.valueOf(value));}
			if (name.equals("lhumerus")) {lRangeRider.setHumerus(Float.valueOf(value));}
			if (name.equals("lforearm")) {lRangeRider.setForearm(Float.valueOf(value));}
			if (name.equals("ltotalarm")) {lRangeRider.setTotalarm(Float.valueOf(value));}
			if (name.equals("lfemur")) {lRangeRider.setFemur(Float.valueOf(value));}
			if (name.equals("ltibia")) {lRangeRider.setTibia(Float.valueOf(value));}
			if (name.equals("linseam")) {lRangeRider.setInseam(Float.valueOf(value));}
			if (name.equals("lfootlength")) {lRangeRider.setFootlength(Float.valueOf(value));}
			
			if (name.equals("utotalbodyheight")) {uRangeRider.setTotalbodyheight(Float.valueOf(value));}
			if (name.equals("usternalnotch")) {uRangeRider.setSternalnotch(Float.valueOf(value));}
			if (name.equals("ushoulderwidth")) {uRangeRider.setShoulderwidth(Float.valueOf(value));}
			if (name.equals("utrunk")) {uRangeRider.setTrunk(Float.valueOf(value));}
			if (name.equals("uhumerus")) {uRangeRider.setHumerus(Float.valueOf(value));}
			if (name.equals("uforearm")) {uRangeRider.setForearm(Float.valueOf(value));}
			if (name.equals("utotalarm")) {uRangeRider.setTotalarm(Float.valueOf(value));}
			if (name.equals("ufemur")) {uRangeRider.setFemur(Float.valueOf(value));}
			if (name.equals("utibia")) {uRangeRider.setTibia(Float.valueOf(value));}
			if (name.equals("uinseam")) {uRangeRider.setInseam(Float.valueOf(value));}
			if (name.equals("ufootlength")) {uRangeRider.setFootlength(Float.valueOf(value));}

			// a bit of spam checking (this field is hidden in our form and should be empty)
			if (name.equals("bread")) {
				if (!"".equals(value)) {
					log.debug("Possible spam, bread field was not empty");
					return "similarrider";
				}
			}
		}
		
		// If the user did not check the checkbox, we'll set the range wide open to get any node
		if (!totalBodyHeightFlag) {lRangeRider.setTotalbodyheight(0); uRangeRider.setTotalbodyheight(999);}
		if (!sternalNotchFlag) {lRangeRider.setSternalnotch(0); uRangeRider.setSternalnotch(999);}
		if (!shoulderWidthFlag) {lRangeRider.setShoulderwidth(0); uRangeRider.setShoulderwidth(999);}
		if (!trunkFlag) {lRangeRider.setTrunk(0); uRangeRider.setTrunk(999);}
		if (!humerusFlag) {lRangeRider.setHumerus(0); uRangeRider.setHumerus(999);}
		if (!forearmFlag) {lRangeRider.setForearm(0); uRangeRider.setForearm(999);}
		if (!totalArmFlag) {lRangeRider.setTotalarm(0); uRangeRider.setTotalarm(999);}
		if (!femureFlag) {lRangeRider.setFemur(0); uRangeRider.setFemur(999);}
		if (!tibiaFlag) {lRangeRider.setTibia(0); uRangeRider.setTibia(999);}
		if (!inseamFlag) {lRangeRider.setInseam(0); uRangeRider.setInseam(999);}
		if (!footLengthFlag) {lRangeRider.setFootlength(0); uRangeRider.setFootlength(999);}

		ArrayList similarRiders = kDTreeService.getSimilarRidersFromRange(lRangeRider, uRangeRider);
		model.addAttribute("similarRiders", similarRiders);
		
		// TODO this business of checking for a string (which indicates that user is not authenticated) is a total hack and should be factored out
		if (! (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof String)) {
			UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
			User user = dao.getUserByemailAddress(principal.getUsername());
			model.addAttribute("emailAddress", user.getEmailaddress());
		}
		
		return "similarriderresults";
	}	
	
	// Return fit form with range values and original value
	@RequestMapping(value = "/similar/fit/{fitid}", method = RequestMethod.GET)
	public String getSimilarFit(@PathVariable Integer fitid, Model model) throws KeySizeException, IllegalArgumentException
	{
		Fit fit = dao.getFitByFitId(fitid);
		model.addAttribute("fit", fit);
		model.addAttribute("lrangefit", BeanUtil.getLowerRange(fit, .015f));
		model.addAttribute("urangefit", BeanUtil.getUpperRange(fit, .015f));
		
		// TODO this business of checking for a string (which indicates that user is not authenticated) is a total hack and should be factored out
		if (! (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof String)) {
			UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
			User user = dao.getUserByemailAddress(principal.getUsername());
			model.addAttribute("emailAddress", user.getEmailaddress());
		}
		
		return "similarfit";
	}
	
	@RequestMapping(value = "/similar/fit/{fitid}", method = RequestMethod.POST)
	public String postSimilarFit(@PathVariable Integer fitid, Model model) throws KeySizeException, IllegalArgumentException
	{
		Fit fit = dao.getFitByFitId(fitid);
		ArrayList similarFits = kDTreeService.getSimilarFits(fit);
		model.addAttribute("similarFits", similarFits);
		
		// TODO this business of checking for a string (which indicates that user is not authenticated) is a total hack and should be factored out
		if (! (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof String)) {
			UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
			User user = dao.getUserByemailAddress(principal.getUsername());
			model.addAttribute("emailAddress", user.getEmailaddress());
		}
		
		return "similarfitresults";
	}
	
	@RequestMapping(value = "/similar/fit/range", method = RequestMethod.POST)
	public String postSimilarFitRange(Model model, HttpServletRequest request) throws KeySizeException, IllegalArgumentException
	{

		Fit lRangeFit = new Fit();
		Fit uRangeFit = new Fit();
		
		//loop through all elements of form
		//assign lRangeFit to lower array and uRangeFit to upper array
		// if checkbox is encountered, set it as a flag that we'll check later
		
		boolean saddleHeightFlag = false;
		boolean centerToBarsFlag = false;
		boolean centerToSaddleFlag = false;
		boolean floorToSaddleFlag = false;
		boolean floorToBarsFlag = false;
		boolean barsWidthFlag = false;

		Enumeration namesEnum = request.getParameterNames();
		while (namesEnum.hasMoreElements()) {
			String name = (String)namesEnum.nextElement();
			String value = request.getParameter(name);
	
			if("saddleheight".equals(name)) {saddleHeightFlag = true;}
			if("centertobars".equals(value)) {centerToBarsFlag = true;}
			if("centertosaddle".equals(value)) {centerToSaddleFlag = true;}
			if("floortosaddle".equals(name)) {floorToSaddleFlag = true;}
			if("floortobars".equals(name)) {floorToBarsFlag = true;}
			if("barswidth".equals(name)) {barsWidthFlag = true;}	
			
			if (name.equals("lsaddleheight")) {lRangeFit.setSaddleheight(Float.valueOf(value));}
			if (name.equals("lcentertobars")) {lRangeFit.setCentertobars(Float.valueOf(value));}
			if (name.equals("lcentertosaddle")) {lRangeFit.setCentertosaddle(Float.valueOf(value));}
			if (name.equals("lfloortosaddle")) {lRangeFit.setFloortosaddle(Float.valueOf(value));}
			if (name.equals("lfloortobars")) {lRangeFit.setFloortobars(Float.valueOf(value));}
			if (name.equals("lbarswidth")) {lRangeFit.setBarswidth(Float.valueOf(value));}
			
			if (name.equals("usaddleheight")) {uRangeFit.setSaddleheight(Float.valueOf(value));}
			if (name.equals("ucentertobars")) {uRangeFit.setCentertobars(Float.valueOf(value));}
			if (name.equals("ucentertosaddle")) {uRangeFit.setCentertosaddle(Float.valueOf(value));}
			if (name.equals("ufloortosaddle")) {uRangeFit.setFloortosaddle(Float.valueOf(value));}
			if (name.equals("ufloortobars")) {uRangeFit.setFloortobars(Float.valueOf(value));}
			if (name.equals("ubarswidth")) {uRangeFit.setBarswidth(Float.valueOf(value));}

			// a bit of spam checking (this field is hidden in our form and should be empty)
			if (name.equals("bread")) {
				if (!"".equals(value)) {
					log.debug("Possible spam, bread field was not empty");
					return "similarrider";
				}
			}
		}
		
		// If the user did not check the checkbox, we'll set the range wide open to get any node
		if (!saddleHeightFlag) {lRangeFit.setSaddleheight(0); uRangeFit.setSaddleheight(999);}
		if (!centerToBarsFlag) {lRangeFit.setCentertobars(0); uRangeFit.setCentertobars(999);}
		if (!centerToSaddleFlag) {lRangeFit.setCentertosaddle(0); uRangeFit.setCentertosaddle(999);}
		if (!floorToSaddleFlag) {lRangeFit.setFloortosaddle(0); uRangeFit.setFloortosaddle(999);}
		if (!floorToBarsFlag) {lRangeFit.setFloortobars(0); uRangeFit.setFloortobars(999);}
		if (!barsWidthFlag) {lRangeFit.setBarswidth(0); uRangeFit.setBarswidth(999);}

		ArrayList similarFits = kDTreeService.getSimilarFitsFromRange(lRangeFit, uRangeFit);
		model.addAttribute("similarFits", similarFits);
		
		// TODO this business of checking for a string (which indicates that user is not authenticated) is a total hack and should be factored out
		if (! (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof String)) {
			UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
			User user = dao.getUserByemailAddress(principal.getUsername());
			model.addAttribute("emailAddress", user.getEmailaddress());
		}
		
		return "similarfitresults";
	}
}